import { Uid } from "../../core/utils";
import { SubjectComponent } from "../../core/subject";
import World from "./world";
import { BulletSide, PhysicsMath } from "./physics_math";
import { EditorAttr } from "./bullet_editor_attribute";

export const LAND_ATTACH_DISTANCE = 25;

const { disallowMultiple, executeInEditMode, ccclass, property } = cc._decorator;

export enum BulletState {
    Physics = 1,
    Grabed = 2,
};

@ccclass
@disallowMultiple
@executeInEditMode
export default class Bullet extends SubjectComponent {
    @property({ displayName: "是否指定外观节点" })
    faceNodeNotRoot = false;
    @property({
        type: cc.Node,
        displayName: "外观节点", visible() {
            return this.faceNodeNotRoot;
        },
    })
    customFaceNode: cc.Node = null;

    get faceNode() {
        return this.customFaceNode || this.node;
    }
    @property()
    speed = cc.v2();
    _color = cc.color(255, 255, 255, 255);
    @EditorAttr("颜色", "color")
    @property()
    get color() {
        return this._color.toHEX("#rrggbbaa");
    }
    set color(hex: string) {
        this._color.fromHEX(hex);
        this.faceNode.color = this._color;
    }

    @EditorAttr("位置")
    get curPos() {
        return cc.v2(this.node.position);
    }
    set curPos(val) {
        this.node.setPosition(val);
    }
    @EditorAttr("大小")
    get curSize() {
        return cc.v2(this.node.width, this.node.height);
    }
    set curSize(val) {
        this.node.width = val.x;
        this.node.height = val.y;
    }
    @EditorAttr("最高速度")
    @property()
    speedMax = cc.v2(2000, 2000);
    @EditorAttr("质量")
    @property()
    mass = 1; //质量密度，影响推动，越重的东西越能推动轻的东西。
    @EditorAttr("阻力")
    @property()
    damping = 3; //速度衰减：speed = speed * (damping/100);
    @EditorAttr("重力系数")
    @property()
    gravityScale = 100; //0-100重力因子：重力*gravityScale = 最终的重力影响
    @EditorAttr("弹力")
    @property
    elastic = 10; //0-99 弹力：碰撞时，速度 * -(elastic/100)
    @EditorAttr("粘力")
    @property
    stick = 0;
    @EditorAttr("浮力")
    @property
    float = 0; //0-100, 0非浮动，100无重力。 影响：Math.max(0,gravity-float)
    @EditorAttr("摩擦力")
    @property
    friction = 0; //0-100 摩擦力：被站时，bullet移动会带动上面物体的移动百分比。
    @EditorAttr("滑动力")
    @property
    slippy = 0; //0-100 滑动力：抗摩擦力（人物走路，或者轮子）friction - slippy

    @EditorAttr("是否动态")
    @property
    isDynamic = true;
    @EditorAttr("感应器")
    @property
    isSensor = false;
    @EditorAttr("可以拿放")
    @property
    isGrabable = false;
    @EditorAttr("可以使用")
    @property
    isUseable = false;

    @EditorAttr("遮挡视线")
    @property
    blockSight = false;

    @EditorAttr("遮挡光线")
    @property
    blockLight = false;

    @EditorAttr("发光")
    @property
    emissive = 0;
    @EditorAttr("光范围")
    @property
    emissiveRange = 0;

    _physicsGroup = 0;
    @EditorAttr("物理组")
    @property
    get physicsGroup() {
        return this._physicsGroup;
    }
    set physicsGroup(val) {
        this._physicsGroup = val;
        if (!val) {
            this.world?.disposeContactsByChild(this);
        }
    }

    @EditorAttr("锁定")
    @property
    lockInEditor = false;

    private __prevRect: cc.Rect = null;
    private __prevRectDirty = false;
    get physicsRect() {
        if (!this.__prevRect || this.__prevRectDirty) {
            this.__prevRect = new cc.Rect();
            this.__prevRect.x = this.node.x - this.node.width * this.node.anchorX;
            this.__prevRect.y = this.node.y - this.node.height * this.node.anchorY;
            this.__prevRect.width = this.node.width;
            this.__prevRect.height = this.node.height;
            this.__prevRectDirty = false;
        }
        return this.__prevRect;
    }

    get physicsWorldRect() {
        let rect = this.physicsRect.clone();
        let wp = this.world.node.convertToWorldSpaceAR(cc.Vec2.ZERO);
        rect.x += wp.x;
        rect.y += wp.y;
        return rect;
    }

    isCreateFromEditor = false; //是否在编辑器中创建。

    bulletUid = "";
    worldStack: World[] = [];
    get world() {
        return this.worldStack[0];
    }
    _state: BulletState = BulletState.Physics;
    _hLookAt: BulletSide = BulletSide.None;
    get hLookAt() {
        return this._hLookAt;
    }
    set hLookAt(val) {
        this._hLookAt = val;
        this.onLookAtChanged(this._hLookAt, this._vLookAt);
    }
    _vLookAt: BulletSide = BulletSide.None;
    get vLookAt() {
        return this._vLookAt;
    }
    set vLookAt(val) {
        this._vLookAt = val;
        this.onLookAtChanged(this._hLookAt, this._vLookAt);
    }

    public get state(): BulletState {
        return this._state;
    }
    public set state(v: BulletState) {
        this._state = v;
        this.emit("state-changed", this._state);

        // if (!val) {
        this.world?.disposeContactsByChild(this);
        // }
    }

    _dockCount = [0, 0, 0, 0]; //上右下左
    get dockCount() {
        return this._dockCount;
    }
    set dockCount(val) {
        this._dockCount = val;
        this.emit("dock-changed", this._dockCount);
        this.onDockCountChanged();
    }
    get dockTotal() {
        return this._dockCount.reduce(function (accumulator, currentValue) {
            return accumulator + currentValue;
        }, 0); // 0 是初始值
    }

    @property({
        serializable: true
    })
    _z = 1; //范围0-99

    @EditorAttr("深度Z")
    @property
    get z() {
        return this._z;
    }
    set z(val) {
        this._z = val;
        this.node.zIndex = this._z;
    }
    @EditorAttr("TAG")
    @property()
    tag = "";
    _physicsEnabled = true;
    @EditorAttr("物理开关")
    @property
    get physicsEnabled() {
        return this._physicsEnabled;
    }
    set physicsEnabled(val) {
        this._physicsEnabled = val;
        if (!val) {
            this.world?.disposeContactsByChild(this);
        }
    }

    @property({
        editorOnly: true,
    })
    editBounding = false;
    @property({
        visible() {
            return this.editBounding;
        },
    })
    get xMin() {
        return this.node.x - this.node.anchorX * this.node.width;
    }
    set xMin(val) {
        this.node.x = val + this.node.anchorX * this.node.width;
    }
    @property({
        visible() {
            return this.editBounding;
        },
    })
    get xMax() {
        return this.node.x + (1 - this.node.anchorX) * this.node.width;
    }
    set xMax(val) {
        this.node.x = val - (1 - this.node.anchorX) * this.node.width;
    }
    @property({
        visible() {
            return this.editBounding;
        },
    })
    get yMin() {
        return this.node.y - this.node.anchorY * this.node.height;
    }
    set yMin(val) {
        this.node.y = val + this.node.anchorY * this.node.height;
    }
    @property({
        visible() {
            return this.editBounding;
        },
    })
    get yMax() {
        return this.node.y + (1 - this.node.anchorX) * this.node.height;
    }
    set yMax(val) {
        this.node.y = val - (1 - this.node.anchorX) * this.node.height;
    }

    // destroy(): boolean {
    //     this.emit("destroy");
    //     if (this.world) {
    //         this.world.removeBullet(this);
    //     }
    //     return super.destroy();
    // }
    protected onDestroy(): void {
        this.emit("destroy");
        //todo: ondestroy时 this.world 是undefined
        // if (this.node?.parent?.getComponent(World)) {
        if (this.world) {
            this.world.removeBullet(this);
        }
    }

    applyForce(force: cc.Vec2) {
        this.speed = this.speed.add(force);
        this.onApplyForce(force);
    }
    prevPos: cc.Vec2 = null;
    deltaPos: cc.Vec2 = cc.v2();
    protected onLoad(): void {
        super.onLoad && super.onLoad();
        this.bulletUid = Uid.getUid();
        if (this.z !== this.node.zIndex) {
            this.node.zIndex = this.z;
        }
        this.node.on(cc.Node.EventType.POSITION_CHANGED, this._onPositionChanged, this);
        this.node.on(cc.Node.EventType.SIZE_CHANGED, this._onSizeChanged, this);
        this.node.on(cc.Node.EventType.COLOR_CHANGED, this._onColorChanged, this);
    }
    _onColorChanged() {
        if (CC_EDITOR) {
            this.color = this.faceNode.color.toHEX("#rrggbbaa");
        }
    }
    _onSizeChanged() {
        this.__prevRectDirty = true;
    }
    _onPositionChanged() {
        this.__prevRectDirty = true;
    }
    grabBy(other: Bullet) {
        this.speed = cc.v2();
        this.state = BulletState.Grabed;
        this.physicsEnabled = false;
        this.dockCount = [0, 0, 0, 0];
        this.emit("on-grab", other);
        this.onGrabBy(other);
    }
    dropBy(other: Bullet) {
        this.physicsEnabled = true;
        this.state = BulletState.Physics;
        this.emit("on-drop", other);
        this.onDropBy(other);
    }
    useBy(other: Bullet) {
        this.emit("on-use", other);
        this.onUseBy(other);
    }
    unuseBy(other: Bullet) {
        this.emit("on-unuse", other);
        this.onUnuseBy(other);
    }

    _onCollisionEnter(contact: BulletContact) {
        if (contact.isDock) {
            this.dockCount[contact.side - 1]++;
        }

        this.onCollisionEnter(contact);
        this.onColEnter && this.onColEnter(contact);
    }
    _onCollisionExit(contact: BulletContact) {
        if (contact.isDock) {
            this.dockCount[contact.side - 1]--;
        }
        this.onCollisionExit(contact);
        this.onColExit && this.onColExit(contact);
    }
    _onCollisionStay(dt: number, contact: BulletContact) {
        if (!this.physicsEnabled) {
            return;
        }

        if (contact.isDockByOther) {
            if (contact.side % 2 == 0) {
                //左右
            }
            else {
                //上下
                contact.other.speed.x *= (1 - Math.max(0, Math.min(100, this.friction - contact.other.slippy)) / 100);
            }
        }

        let massSum = this.mass + contact.other.mass;
        let otherMassPc = this.mass / massSum;
        let myMassPc = 1 - otherMassPc;

        if (this.isDynamic) {
            if (!contact.other.isSensor) {
                let elastic = this.elastic / 100;
                // if (contact.other.isDynamic) {
                //     switch (contact.side) {
                //         case BulletSide.Top:
                //         case BulletSide.Bottom:
                //             this.speed.y = (this.speed.y + contact.other.speed.y) * myMovePc;
                //             break;
                //         case BulletSide.Right:
                //         case BulletSide.Left:
                //             this.speed.x = (this.speed.x + contact.other.speed.x) * myMovePc;
                //             break;
                //     }

                // }
                //反弹
                switch (contact.side) {
                    case BulletSide.Top:
                        if (contact.other.isDynamic) {
                            let dis = Math.abs(this.yMax - contact.other.yMin);
                            this.yMax -= dis * myMassPc;
                        }
                        else {
                            this.yMax = contact.other.yMin;
                        }
                        if (!contact.isDock) {
                            if (this.speed.y > 0) {
                                this.speed.y *= -elastic;
                            }
                        }
                        else {
                            this.speed.y = Math.min(0, this.speed.y);
                        }
                        break;
                    case BulletSide.Bottom:
                        if (contact.other.isDynamic) {
                            let dis = Math.abs(this.yMin - contact.other.yMax);
                            this.yMin += dis * myMassPc;
                        }
                        else {
                            this.yMin = contact.other.yMax;
                        }
                        if (!contact.isDock) {
                            if (this.speed.y < 0) {
                                this.speed.y *= -elastic;
                            }
                        }
                        else {
                            this.speed.y = Math.max(0, this.speed.y);
                        }
                        break;
                    case BulletSide.Right:
                        if (contact.other.isDynamic) {
                            let dis = Math.abs(this.xMax - contact.other.xMin);
                            this.xMax -= dis * myMassPc;
                        }
                        else {
                            this.xMax = contact.other.xMin;
                        }
                        if (!contact.isDock) {
                            if (this.speed.x > 0) {
                                this.speed.x *= -elastic;
                            }
                        }
                        else {
                            this.speed.x = Math.min(0, this.speed.x);
                        }
                        break;
                    case BulletSide.Left:
                        if (contact.other.isDynamic) {
                            let dis = Math.abs(this.xMin - contact.other.xMax);
                            this.xMin += dis * myMassPc;
                        }
                        else {
                            this.xMin = contact.other.xMax;
                        }
                        if (!contact.isDock) {
                            if (this.speed.x < 0) {
                                this.speed.x *= -elastic;
                            }
                        }
                        else {
                            this.speed.x = Math.max(0, this.speed.x);
                        }
                        break;
                }
            }
        }
        this.onCollisionStay(dt, contact);
        this.onColStay && this.onColStay(dt, contact);
    }
    physicsUpdate(dt: number): void {
        if (!this.physicsEnabled) {
            return;
        }

        if (this.isDynamic) {
            let damping = 1 - (Math.min(100, Math.max(0, this.damping)) / 100);

            this.speed.x = Math.max(-this.speedMax.x, Math.min(this.speedMax.x, this.speed.x));
            this.speed.y = Math.max(-this.speedMax.y, Math.min(this.speedMax.y, this.speed.y));
            this.speed = this.speed.add(this.world.gravity.mul(Math.min(100, Math.max(0, this.gravityScale - Math.min(100, Math.max(0, this.float)))) / 100).mul(dt)).mul(damping);

            this.curPos = this.curPos.add(this.speed.mul(dt));
        }
        if (this.prevPos) {
            this.deltaPos = this.curPos.sub(this.prevPos);
        }
        this.prevPos = this.curPos;

        this.onPhysicsUpdate(dt);
    }
    //地图编辑器

    //缩放触发
    onMapEditorSizing() {
    }
    //移动触发
    onMapEditorMove() { }
    //设置属性
    onMapEditorSetAttr(key: string) { }

    onLookAtChanged(h: BulletSide, v: BulletSide) { }
    onDockCountChanged() { }
    onApplyForce(force: cc.Vec2) { }
    onUseBy(other: Bullet) { }
    onUnuseBy(other: Bullet) { }
    onGrabBy(other: Bullet) { }
    onDropBy(other: Bullet) { }
    onEnterStand() { }
    onChangeStand() { }
    onCollisionEnter(contact: BulletContact) { }
    onCollisionStay(dt: number, contact: BulletContact) { }
    onCollisionExit(contact: BulletContact) { }
    onColEnter: (contact: BulletContact) => void = null;
    onColExit: (contact: BulletContact) => void = null;
    onColStay: (dt: number, contact: BulletContact) => void = null;
    onExitWorld() { }
    onEnterWorld() { }
    onPhysicsUpdate(dt: number) { }

    enterWorldByParent() { //world已经是父节点的时候进入世界
        let world = this.node.parent?.getComponent(World);
        if (world && !this.worldStack.includes(world)) {
            this.worldStack.unshift(world);
            world.addBullet(this);
        }
    }
    enterWorld(world: World) {
        if (!this.worldStack.includes(world)) {
            this.worldStack.unshift(world);
            world.addBullet(this);
            this._onEnterWorld();
        }
    }
    exitWorld() {
        if (this.world) {
            this.world.removeBullet(this);
            this.worldStack.shift();
            this._onExitWorld();
        }
    }
    _onExitWorld() {
        this.node.removeFromParent(false);
        this.emit("exit-world");
        this.onExitWorld();
    }
    _onEnterWorld() {
        this.node.setPosition(this.world.node.convertToNodeSpaceAR(this.node.convertToWorldSpaceAR(cc.Vec2.ZERO)));
        this.world.node.addChild(this.node);
        this.emit("enter-world", this.world);
        this.onEnterWorld();
    }
};

export class BulletContact {
    valid = true;
    a: Bullet = null;
    b: Bullet = null;

    target: Bullet = null;
    setTarget(target: Bullet) {
        this.target = target;
    }

    get self() {
        if (this.target) {
            return this.target === this.a ? this.a : this.b;
        }
        else {
            console.warn("没有setTarget");
            return null;
        }
    }
    get other() {
        if (this.target) {
            return this.target === this.a ? this.b : this.a;
        }
        else {
            console.warn("没有setTarget");
            return null;
        }
    }
    abSide: BulletSide = BulletSide.None;
    get side(): BulletSide {
        if (this.target === this.a) {
            return this.abSide;
        }
        else {
            let side = (this.abSide + 2);
            if (side > 4) {
                side %= 4;
            }
            return side;
        }
    }
    //粘力
    stickGrip = 0;

    abDock = false;
    baDock = false;
    //停靠（站立/扶墙/黏住）
    get isDock() {
        if (this.target === this.a) {
            return this.abDock;
        }
        else if (this.target === this.b) {
            return this.baDock;
        }
        else {
            console.warn("没有setTarget");
            return null;
        }
    }
    get isDockByOther() {
        if (this.target === this.a) {
            return this.baDock;
        }
        else if (this.target === this.b) {
            return this.abDock;
        }
        else {
            console.warn("没有setTarget");
            return null;
        }
    }
    //碰撞时速度
    speedMag = 0;

    stay(dt: number) {
        if (cc.isValid(this.a)) {
            this.setTarget(this.a);
            this.a._onCollisionStay && this.a._onCollisionStay(dt, this);
        }
        if (cc.isValid(this.b)) {
            this.setTarget(this.b);
            this.b._onCollisionStay && this.b._onCollisionStay(dt, this);
        }
    }
    enter() {
        if (cc.isValid(this.a)) {
            this.setTarget(this.a);
            this.a._onCollisionEnter && this.a._onCollisionEnter(this);
        }
        if (cc.isValid(this.b)) {
            this.setTarget(this.b);
            this.b._onCollisionEnter && this.b._onCollisionEnter(this);
        }
    }
    exit() {
        if (!this.valid) {
            return;
        }
        this.valid = false;

        if (cc.isValid(this.a)) {
            this.setTarget(this.a);
            this.a._onCollisionExit && this.a._onCollisionExit(this);
        }
        if (cc.isValid(this.b)) {
            this.setTarget(this.b);
            this.b._onCollisionExit && this.b._onCollisionExit(this);
        }
    }
};

